from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.db.models import Q
from django.http import Http404
from django.db.models import Avg, Count
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from shop.models import Product, Category


def _apply_pagination_and_sorting(request, query):
    # sort query
    sort = request.GET.get('sort', None)
    if sort == 'price':
        query = query.order_by("price")
    elif sort == 'price_reverse':
        query = query.order_by('price').reverse()
    elif sort == 'rating':
        query = query.order_by('rating_stars').reverse()
    elif sort == 'rating_reverse':
        query = query.order_by('rating_stars')
    elif sort == 'name_reverse':
        query = query.order_by('name').reverse()
    else:
        # default sorting is by name
        sort = 'name'
        query = query.order_by('name')

    # loads products from selected category into Paginator
    pages = Paginator(query, 8)

    # defaults to page 1 if page is invalid
    try:
        page = int(request.GET['page'])
    except (KeyError, ValueError):
        page = 1

    # loads requested page, defaults to page 1 on on invalid page number
    try:
        product_pages = pages.page(page)
    except (EmptyPage, InvalidPage):
        product_pages = pages.page(1)

    return {'products': product_pages, 'sort': sort}


def _get_product_query(request):
    """Builds a Product query that includes comment count, average rating,
    vote count and current user's rating of the product.

    """
    query = Product.objects.annotate(rating_stars=Avg('rating__stars'),
                                     rating_votes=Count('rating'))

    comment_count_subquery = (
        'SELECT COUNT(*) FROM threadedcomments_threadedcomment AS c '
        'WHERE (c.is_public = 1 OR c.is_approved = 1) AND '
        'c.object_id = shop_product.id AND c.content_type_id = 14')
    query = query.extra(select={'comment_count': comment_count_subquery})

    if request.user.is_authenticated():
        user_rating_subquery = (
            'SELECT stars FROM shop_rating '
            'WHERE shop_rating.product_id = shop_product.id AND '
            'shop_rating.user_id = %s') % int(request.user.id)

        query = query.extra(select={'userrating': user_rating_subquery})

    return query


def list(request, category_id):
    query = _get_product_query(request)

    if category_id is None:
        category = None
        query = query.all()
    else:
        category = get_object_or_404(Category, pk=category_id)
        query = query.filter(category=category).all()

    data = _apply_pagination_and_sorting(request, query)
    data['category'] = category

    return render_to_response('products.html', data,
                              context_instance=RequestContext(request))


def detail(request, product_id):
    # get product
    try:
        product = _get_product_query(request).get(id=product_id)
    except Product.DoesNotExist:
        raise Http404

    return render_to_response('product.html',
                              {'product': product},
                              context_instance=RequestContext(request))

def search(request):
    # query search
    search = request.GET.get('q', None)
    try:
        category_id = int(request.GET['category_id'])
    except (ValueError, KeyError):
        category_id = None

    if search:
        if category_id:
            queryset = ((Q(name__icontains=search) | Q(description__icontains=search)) & Q(category__exact=category_id))
        else:
            queryset = (Q(name__icontains=search) | Q(description__icontains=search))
        query = _get_product_query(request).filter(queryset).distinct()
    else:
        query = Product.objects.none()

    data = _apply_pagination_and_sorting(request, query)
    data['q'] = search
    data['category_id'] = category_id

    return render_to_response('search_results.html', data,
                              context_instance=RequestContext(request))

